package com.j.lemon.learn.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.*;

/**
 * @Author lijunjun
 * @Date 2019-05-29 15:24
 * @Description
 */
public class GaoDeUtil {

    private static final Logger logger = LoggerFactory.getLogger(GaoDeUtil.class);

    private static final String URL = "http://restapi.amap.com/v3/";

    private static final String KEY = "2fb14b84cd5bfc260beef009d9be4880";


    public static void main(String[] args) throws Exception {


        //根据地址解析经纬度
//        String start = "河南省焦作市太极路正大国贸中心";
//        String end = "河南省焦作市解放区新华中街供应处家属院";
//        String startLonLat = getLonLat(start);
//        String endLonLat = getLonLat(end);
//        System.out.println(start+":"+startLonLat);
//        System.out.println(end+":"+endLonLat);
//        System.out.println("-----------------------------------");
//
//        //根据经纬度计算距离
//        Long dis = getDistance(startLonLat, endLonLat);
//        System.out.println(start+"和"+end+"之间的距离是"+dis+"米");
//        System.out.println("-----------------------------------");
//
//
//        String[] addresses = new String[]{"北京中国工商银行软件研发中心", "天安门", "北京海淀城建大厦"};
//        //传入地址列表，返回坐标列表
//        Map<String, String> lonLats = getLonLats(addresses);
//        List<String> sets = new ArrayList<>();
//        if (lonLats != null) {
//            lonLats.forEach((k, v) -> {
//                System.out.println(k + ":" + v);
//                sets.add(v);
//            });
//        } else {
//            System.out.println("调用失败");
//        }
//        System.out.println("-----------------------------------");
//
//        //输入一个坐标和一个坐标列表，返回坐标与列表中坐标的最近距离
//        Long minDistance = getMinDistance(startLonLat, sets);
//        if(minDistance!=null){
//            System.out.println("最近距离："+minDistance+"米");
//        }else{
//            System.out.println("调用失败");
//        }

        String addr = "那舅小学";
        String lonLat = getLonLat(addr);
        System.out.println(lonLat);

        Long distance = getDistance("108.453140,22.834972", "108.344032,22.852565");
        Long distance1 = getDistance("108.453140,22.834972", "108.453140,22.834972");

        System.out.println(distance);
        System.out.println(distance1);

        double v = calculateLineDistance(new Double[]{108.453140, 22.834972}, new Double[]{108.344032, 22.852565});
        System.out.println(v);

    }


    /**
     * 输入地址address,返回经纬度信息, 格式是 经度,纬度
     *
     * @param address
     * @return 成功则返回经纬度，格式：经度,纬度
     * 传入高德接口无法识别的地址，则返回null
     * 高德接口返回值status不=1（授权失败，参数名称错误等），抛出异常
     */
    public static String getLonLat(String address) {
        String url = URL + "geocode/geo";
        Map<String, String> params = new HashMap<>();
        params.put("key", KEY);
        params.put("address", address);
        String queryResult = doGet(url, params);//高德接口返回的是JSON格式的字符串
        System.out.println(queryResult);
        if (queryResult != null) {
            JSONObject jo = JSONObject.parseObject(queryResult);
            String status = jo.getString("status");
            if ("1".equals(status)) {
                JSONArray ja = jo.getJSONArray("geocodes");
                if (ja.size() > 0) {
                    return JSONObject.parseObject(ja.getString(0)).get("location").toString();
                } else {
                    logger.error("未查询到改地址对应的经纬度，请检查地址格式！");
                }
            } else {
                logger.error("调用高德地址解析接口返回错误信息：{}", jo.getString("info"));
            }
        }
        return null;
    }

    /**
     * 输入地址列表，返回纬度坐标列表
     *
     * @param addresses
     * @return 成功则返回地址和对应的经纬度map，失败（有一个地址解析失败）则返回null
     */
    public static Map<String, String> getLonLats(String[] addresses) {
        Map<String, String> lonLats = new HashMap<>();
        for (String address : addresses) {
            String lonLat = getLonLat(address);
            if (lonLat == null) {
                return null;
            }
            lonLats.put(address, lonLat);
        }
        return lonLats;
    }


    /**
     * 传入两个坐标，返回距离
     *
     * @param startLonLat
     * @param endLonLat
     * @return 成功则返回距离（米）
     * 失败则返回null
     */
    public static Long getDistance(String startLonLat, String endLonLat) {
        String url = URL + "distance";
        Map<String, String> params = new HashMap<>();
        params.put("key", KEY);
        params.put("origins", startLonLat);
        params.put("destination", endLonLat);
        String queryResult = doGet(url, params);
        if (queryResult != null) {
            JSONObject jo = JSONObject.parseObject(queryResult);
            String status = jo.getString("status");
            if ("1".equals(status)) {
                JSONArray ja = jo.getJSONArray("results");
                if (ja.size() > 0) {
                    return Long.parseLong(JSONObject.parseObject(ja.getString(0)).get("distance").toString());
                } else {
                    logger.error("未查到两个坐标的距离，请检查坐标格式！");
                }
            } else {
                logger.error("调用高德地址距离接口返回错误信息：{}", jo.getString("info"));
            }
        }
        return null;
    }

    /**
     * 输入一个坐标A，和一个坐标列表B，返回B里面离A最近的距离
     *
     * @param lonlat
     * @param lonlats
     * @return 成功则返回最近距离（米），失败（有两个坐标计算距离时出错）则返回null
     */
    public static Long getMinDistance(String lonlat, List<String> lonlats) {
        List<Long> distances = new ArrayList<>();
        for (String eachLonlat : lonlats) {
            Long distance = getDistance(lonlat, eachLonlat);
            if (distance == null) {
                return null;
            }
            distances.add(distance);
        }
        Optional<Long> min = distances.stream().min(Comparator.comparingLong(l -> l));
        return min.orElse(null);
    }


    private static String doGet(String url, Map<String, String> params) {
        if (params != null && !params.isEmpty()) {
            StringBuilder urlBuilder = new StringBuilder(url);
            for (Map.Entry entry : params.entrySet()) {
                String key = entry.getKey().toString();
                String value = entry.getValue().toString();
                if (urlBuilder.indexOf("?") > 0) {
                    urlBuilder.append("&").append(key).append("=").append(value);
                } else {
                    urlBuilder.append("?").append(key).append("=").append(value);
                }
            }
            url = urlBuilder.toString();
        }
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        String result = null;

        try {
            Response response = retryProcess(okHttpClient, request, 3);
            if (null != response.body()) {
                result = response.body().string();
            }
        } catch (IOException e) {
            logger.error("调用高德接口失败，url:{}", url, e);
        }
        return result;


    }

    /**
     * @param request
     * @param retry
     * @return
     * @throws IOException
     */
    private static Response retryProcess(OkHttpClient okHttpClient, Request request, int retry) throws IOException {
        Response response = null;
        IOException ioE = null;
        if (retry > 0) {
            while (retry > 0) {
                try {
                    Call call = okHttpClient.newCall(request);
                    response = call.execute();
                } catch (IOException e) {
                    response = null;
                    logger.error("链接超时或失败 url:" + request.url());
                    ioE = e;
                }
                retry--;
                if (null != response) {
                    break;
                }
            }
            if (response != null) {
                return response;
            } else {
                throw ioE;
            }
        } else {
            Call call = okHttpClient.newCall(request);
            return call.execute();
        }
    }

    /**
     * 根据用户的起点和终点经纬度计算两点间距离，此距离为相对较短的距离，单位米。
     *
     * @param start
     *           起点的坐标
     * @param end
     *           终点的坐标
     * @return
     */
    public static double calculateLineDistance(Double [] start, Double []  end)
    {
        if ((start == null) || (end == null))
        {
            throw new IllegalArgumentException("非法坐标值，不能为null");
        }
        //
        double d1 = 0.01745329251994329D;
        double d2 = start[0];
        double d3 = start[1];
        double d4 = end[0];
        double d5 = end[1];
        d2 *= d1;
        d3 *= d1;
        d4 *= d1;
        d5 *= d1;
        double d6 = Math.sin(d2);
        double d7 = Math.sin(d3);
        double d8 = Math.cos(d2);
        double d9 = Math.cos(d3);
        double d10 = Math.sin(d4);
        double d11 = Math.sin(d5);
        double d12 = Math.cos(d4);
        double d13 = Math.cos(d5);
        double[] arrayOfDouble1 = new double[3];
        double[] arrayOfDouble2 = new double[3];
        arrayOfDouble1[0] = (d9 * d8);
        arrayOfDouble1[1] = (d9 * d6);
        arrayOfDouble1[2] = d7;
        arrayOfDouble2[0] = (d13 * d12);
        arrayOfDouble2[1] = (d13 * d10);
        arrayOfDouble2[2] = d11;
        double d14 = Math.sqrt((arrayOfDouble1[0] - arrayOfDouble2[0]) * (arrayOfDouble1[0] - arrayOfDouble2[0])
                + (arrayOfDouble1[1] - arrayOfDouble2[1]) * (arrayOfDouble1[1] - arrayOfDouble2[1])
                + (arrayOfDouble1[2] - arrayOfDouble2[2]) * (arrayOfDouble1[2] - arrayOfDouble2[2]));

        return (Math.asin(d14 / 2.0D) * 12742001.579854401D);
    }


}
